/*
 * MIT License
 *
 * Copyright (c) 2023 北京凯特伟业科技有限公司
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.je.auth;

import com.je.auth.check.exception.ApiDisabledException;
import com.je.auth.check.exception.DisableLoginException;
import com.je.auth.check.exception.NotLoginException;
import com.je.auth.check.exception.TokenException;
import com.je.auth.config.CookieConfig;
import com.je.auth.config.TokenConfig;
import com.je.auth.check.context.model.AuthCookie;
import com.je.auth.check.context.model.AuthRequest;
import com.je.auth.check.context.model.AuthStorage;
import com.je.auth.check.fun.AuthFunction;
import com.je.auth.listener.AuthEvent;
import com.je.auth.stp.AuthLoginModel;
import com.je.auth.stp.AuthTokenInfo;
import com.je.auth.check.util.FoxUtil;
import com.je.common.auth.token.AuthSession;
import com.je.common.auth.token.TokenSign;
import java.util.*;
import java.util.function.Consumer;

/**
 * 统一门面管理器
 */
public interface AuthLoginManager {

    AuthEngine getAuthEngine();

    void setAuthEngine(AuthEngine authEngine);

    // ------------------- 获取token 相关 -------------------

    /**
     * 返回token名称
     *
     * @return 此StpLogic的token名称
     */
    default String getTokenName() {
        return splicingKeyTokenName();
    }

    default String createTokenValue(Object loginId, String device, long timeout, Map<String, Object> extraData) {
        // 根据配置的tokenStyle生成不同风格的token
        String tokenStyle = getConfig().getTokenStyle();
        // uuid
        if (TokenConsts.TOKEN_STYLE_UUID.equals(tokenStyle)) {
            return UUID.randomUUID().toString();
        }
        // 简单uuid (不带下划线)
        if (TokenConsts.TOKEN_STYLE_SIMPLE_UUID.equals(tokenStyle)) {
            return UUID.randomUUID().toString().replaceAll("-", "");
        }
        // 32位随机字符串
        if (TokenConsts.TOKEN_STYLE_RANDOM_32.equals(tokenStyle)) {
            return FoxUtil.getRandomString(32);
        }
        // 64位随机字符串
        if (TokenConsts.TOKEN_STYLE_RANDOM_64.equals(tokenStyle)) {
            return FoxUtil.getRandomString(64);
        }
        // 128位随机字符串
        if (TokenConsts.TOKEN_STYLE_RANDOM_128.equals(tokenStyle)) {
            return FoxUtil.getRandomString(128);
        }
        // tik风格 (2_14_16)
        if (TokenConsts.TOKEN_STYLE_TIK.equals(tokenStyle)) {
            return FoxUtil.getRandomString(2) + "_" + FoxUtil.getRandomString(14) + "_" + FoxUtil.getRandomString(16) + "__";
        }
        // 默认，还是uuid
        return UUID.randomUUID().toString();
    }

    /**
     * 在当前会话写入当前TokenValue
     *
     * @param tokenValue token值
     */
    default void setTokenValue(String tokenValue) {
        setTokenValue(tokenValue, (int) getAuthEngine().getConfig().getTimeout());
    }

    /**
     * 在当前会话写入当前TokenValue
     *
     * @param tokenValue    token值
     * @param cookieTimeout Cookie存活时间(秒)
     */
    default void setTokenValue(String tokenValue, int cookieTimeout) {
        if (FoxUtil.isEmpty(tokenValue)) {
            return;
        }

        // 1. 将token保存到[存储器]里
        setTokenValueToStorage(tokenValue);

        // 2. 将 Token 保存到 [Cookie] 里
        if (getAuthEngine().getAuthCheckEngine().getConfig().getIsReadCookie()) {
            setTokenValueToCookie(tokenValue, cookieTimeout);
        }
    }

    /**
     * 将 Token 保存到 [Storage] 里
     *
     * @param tokenValue token值
     */
    default void setTokenValueToStorage(String tokenValue) {
        // 1. 将token保存到[存储器]里
        AuthStorage storage = getAuthEngine().getAuthCheckEngine().getAuthTokenContext().getStorage();

        // 2. 如果打开了 Token 前缀模式，则拼接上前缀
        String tokenPrefix = getConfig().getTokenPrefix();
        if (FoxUtil.isEmpty(tokenPrefix) == false) {
            storage.set(splicingKeyJustCreatedSave(), tokenPrefix + TokenConsts.TOKEN_CONNECTOR_CHAT + tokenValue);
        } else {
            storage.set(splicingKeyJustCreatedSave(), tokenValue);
        }

        // 3. 写入 (无前缀)
        storage.set(TokenConsts.JUST_CREATED_NOT_PREFIX, tokenValue);
    }

    /**
     * 将 Token 保存到 [Cookie] 里
     *
     * @param tokenValue    token值
     * @param cookieTimeout Cookie存活时间(秒)
     */
    default void setTokenValueToCookie(String tokenValue, int cookieTimeout) {
        CookieConfig cfg = getConfig().getCookie();
        AuthCookie cookie = new AuthCookie()
                .setName(getTokenName())
                .setValue(tokenValue)
                .setMaxAge(cookieTimeout)
                .setDomain(cfg.getDomain())
                .setPath(cfg.getPath())
                .setSecure(cfg.getSecure())
                .setHttpOnly(cfg.getHttpOnly())
                .setSameSite(cfg.getSameSite());
        getAuthEngine().getAuthCheckEngine().getAuthTokenContext().getResponse().addCookie(cookie);
    }

    /**
     * 获取当前TokenValue
     *
     * @return 当前tokenValue
     */
    default String getTokenValue() {
        // 1. 获取
        String tokenValue = getTokenValueNotCut();

        // 2. 如果打开了前缀模式，则裁剪掉
        String tokenPrefix = getConfig().getTokenPrefix();
        if (FoxUtil.isEmpty(tokenPrefix) == false) {
            // 如果token并没有按照指定的前缀开头，则视为未提供token
            if (FoxUtil.isEmpty(tokenValue) || tokenValue.startsWith(tokenPrefix + TokenConsts.TOKEN_CONNECTOR_CHAT) == false) {
                tokenValue = null;
            } else {
                // 则裁剪掉前缀
                tokenValue = tokenValue.substring(tokenPrefix.length() + TokenConsts.TOKEN_CONNECTOR_CHAT.length());
            }
        }

        // 3. 返回
        return tokenValue;
    }

    /**
     * 获取当前TokenValue (不裁剪前缀)
     *
     * @return /
     */
    default String getTokenValueNotCut() {
        // 0. 获取相应对象
        AuthStorage storage = getAuthEngine().getAuthCheckEngine().getAuthTokenContext().getStorage();
        AuthRequest request = getAuthEngine().getAuthCheckEngine().getAuthTokenContext().getRequest();
        TokenConfig config = getConfig();
        String keyTokenName = getTokenName();
        String tokenValue = null;

        // 1. 尝试从Storage里读取
        if (storage.get(splicingKeyJustCreatedSave()) != null) {
            tokenValue = String.valueOf(storage.get(splicingKeyJustCreatedSave()));
        }
        // 2. 尝试从请求体里面读取
        if (tokenValue == null && getAuthEngine().getAuthCheckEngine().getConfig().getIsReadBody()) {
            tokenValue = request.getParam(keyTokenName);
        }
        // 3. 尝试从header里读取
        if (tokenValue == null && getAuthEngine().getAuthCheckEngine().getConfig().getIsReadHead()) {
            tokenValue = request.getHeader(keyTokenName);
        }
        // 4. 尝试从cookie里读取
        if (tokenValue == null && getAuthEngine().getAuthCheckEngine().getConfig().getIsReadCookie()) {
            tokenValue = request.getCookieValue(keyTokenName);
        }

        // 5. 返回
        return tokenValue;
    }

    /**
     * 获取当前会话的Token信息
     *
     * @return token信息
     */
    default AuthTokenInfo getTokenInfo() {
        AuthTokenInfo info = new AuthTokenInfo();
        info.tokenName = getTokenName();
        info.tokenValue = getTokenValue();
        info.isLogin = isLogin();
        info.loginId = getLoginIdDefaultNull();
        info.tokenTimeout = getTokenTimeout();
        info.sessionTimeout = getSessionTimeout();
        info.tokenSessionTimeout = getTokenSessionTimeout();
        info.tokenActivityTimeout = getTokenActivityTimeout();
        info.loginDevice = getLoginDevice();
        return info;
    }


    // ------------------- 登录相关操作 -------------------

    // --- 登录

    /**
     * 会话登录
     *
     * @param id 账号id，建议的类型：（long | int | String）
     */
    default void login(Object id) {
        login(id, new AuthLoginModel().setDevice(TokenConsts.DEFAULT_LOGIN_DEVICE));
    }

    /**
     * 会话登录，并指定登录设备
     *
     * @param id     账号id，建议的类型：（long | int | String）
     * @param device 设备标识
     */
    default void login(Object id, String device) {
        login(id, new AuthLoginModel().setDevice(device));
    }

    /**
     * 会话登录，并指定是否 [记住我]
     *
     * @param id              账号id，建议的类型：（long | int | String）
     * @param isLastingCookie 是否为持久Cookie
     */
    default void login(Object id, boolean isLastingCookie) {
        login(id, new AuthLoginModel().setIsLastingCookie(isLastingCookie));
    }

    /**
     * 会话登录，并指定所有登录参数Model
     *
     * @param id         登录id，建议的类型：（long | int | String）
     * @param loginModel 此次登录的参数Model
     */
    default void login(Object id, AuthLoginModel loginModel) {
        // 1、创建会话
        String token = createLoginSession(id, loginModel);

        // 2、在当前客户端注入Token
        setTokenValue(token, loginModel.getCookieTimeout());
    }

    /**
     * 创建指定账号id的登录会话
     *
     * @param id 登录id，建议的类型：（long | int | String）
     * @return 返回会话令牌
     */
    default String createLoginSession(Object id) {
        return createLoginSession(id, new AuthLoginModel());
    }

    /**
     * 创建指定账号id的登录会话
     *
     * @param id         登录id，建议的类型：（long | int | String）
     * @param loginModel 此次登录的参数Model
     * @return 返回会话令牌
     */
    default String createLoginSession(Object id, AuthLoginModel loginModel) {

        TokenException.throwByNull(id, "账号id不能为空");

        // ------ 0、前置检查：如果此账号已被封禁.
        if (isDisable(id)) {
            throw new DisableLoginException(id, getDisableTime(id));
        }

        // ------ 1、初始化 loginModel
        TokenConfig config = getConfig();
        loginModel.configBuild(config);

        // ------ 2、生成一个token
        String tokenValue = null;
        // --- 如果允许并发登录
        if (config.getIsConcurrent()) {
            // 如果配置为共享token, 则尝试从Session签名记录里取出token
            if (getConfigOfIsShare()) {
                tokenValue = getTokenValueByLoginId(id, loginModel.getDeviceOrDefault());
            }
        } else {
            // --- 如果不允许并发登录，则将这个账号的历史登录标记为：被顶下线
            replaced(id, loginModel.getDevice());
        }

        // 如果至此，仍未成功创建tokenValue, 则开始生成一个
        if (tokenValue == null) {
            if (FoxUtil.isEmpty(loginModel.getToken())) {
                tokenValue = createTokenValue(id, loginModel.getDevice(), loginModel.getTimeout(), loginModel.getExtraData());
            } else {
                tokenValue = loginModel.getToken();
            }
        }

        // ------ 3. 获取 User-Session , 续期
        AuthSession session = getSessionByLoginId(id, true);
        getAuthEngine().getAuthSessionTemplate().updateMinTimeout(session.getId(), loginModel.getTimeout());
        // 在 User-Session 上记录token签名
        getAuthEngine().getAuthSessionTemplate().addTokenSign(session, tokenValue, loginModel.getDeviceOrDefault());

        // ------ 4. 持久化其它数据
        // token -> id 映射关系
        saveTokenToIdMapping(tokenValue, id, loginModel.getTimeout());

        // 写入 [token-last-activity]
        setLastActivityToNow(tokenValue);

        // $$ 通知监听器，账号xxx 登录成功
        AuthEvent authEvent = new AuthEvent();
        authEvent.setEventType(AuthEvent.AuthEventType.LOGIN);
        authEvent.setAuthLoginModel(loginModel);
        authEvent.setLoginId(id);
        getAuthEngine().trigger(authEvent);
        // 返回Token
        return tokenValue;
    }

    // --- 注销

    /**
     * 会话注销
     */
    default void logout() {
        // 如果连token都没有，那么无需执行任何操作
        String tokenValue = getTokenValue();
        if (FoxUtil.isEmpty(tokenValue)) {
            return;
        }

        // 从当前 [storage存储器] 里删除
        getAuthEngine().getAuthCheckEngine().getAuthTokenContext().getStorage().delete(splicingKeyJustCreatedSave());

        // 如果打开了Cookie模式，则把cookie清除掉
        if (getAuthEngine().getAuthCheckEngine().getConfig().getIsReadCookie()) {
            getAuthEngine().getAuthCheckEngine().getAuthTokenContext().getResponse().deleteCookie(getTokenName());
        }

        // 清除这个token的相关信息
        logoutByTokenValue(tokenValue);
    }

    /**
     * 会话注销，根据账号id
     *
     * @param loginId 账号id
     */
    default void logout(Object loginId) {
        logout(loginId, null);
    }

    /**
     * 会话注销，根据账号id 和 设备标识
     *
     * @param loginId 账号id
     * @param device  设备标识 (填null代表所有注销设备)
     */
    default void logout(Object loginId, String device) {
        clearTokenCommonMethod(loginId, device, tokenValue -> {
            // 删除Token-Id映射 & 清除Token-Session
            deleteTokenToIdMapping(tokenValue);
            deleteTokenSession(tokenValue);

            AuthEvent authEvent = new AuthEvent();
            authEvent.setEventType(AuthEvent.AuthEventType.LOGOUT);
            authEvent.setLoginId(loginId);
            authEvent.setTokenValue(tokenValue);
            getAuthEngine().trigger(authEvent);
        }, true);
    }

    /**
     * 会话注销，根据指定 Token
     *
     * @param tokenValue 指定token
     */
    default void logoutByTokenValue(String tokenValue) {
        // 1. 清理 token-last-activity
        clearLastActivity(tokenValue);

        // 2. 注销 Token-Session
        deleteTokenSession(tokenValue);

        // if. 无效 loginId 立即返回
        String loginId = getLoginIdNotHandle(tokenValue);
        if (isValidLoginId(loginId) == false) {
            if (loginId != null) {
                deleteTokenToIdMapping(tokenValue);
            }
            return;
        }
        // 3. 清理token-id索引
        deleteTokenToIdMapping(tokenValue);

        // $$ 通知监听器，某某Token注销下线了
        AuthEvent authEvent = new AuthEvent();
        authEvent.setEventType(AuthEvent.AuthEventType.LOGOUT);
        authEvent.setLoginId(loginId);
        authEvent.setTokenValue(tokenValue);
        getAuthEngine().trigger(authEvent);

        // 4. 清理User-Session上的token签名 & 尝试注销User-Session
        AuthSession session = getSessionByLoginId(loginId, false);
        if (session != null) {
            getAuthEngine().getAuthSessionTemplate().removeTokenSign(session, tokenValue);
            getAuthEngine().getAuthSessionTemplate().logoutByTokenSignCountToZero(session);
        }
    }

    /**
     * 踢人下线，根据账号id
     * <p> 当对方再次访问系统时，会抛出NotLoginException异常，场景值=-5 </p>
     *
     * @param loginId 账号id
     */
    default void kickout(Object loginId) {
        kickout(loginId, null);
    }

    /**
     * 踢人下线，根据账号id 和 设备标识
     * <p> 当对方再次访问系统时，会抛出NotLoginException异常，场景值=-5 </p>
     *
     * @param loginId 账号id
     * @param device  设备标识 (填null代表踢出所有设备)
     */
    default void kickout(Object loginId, String device) {
        clearTokenCommonMethod(loginId, device, tokenValue -> {
            // 将此 token 标记为已被踢下线
            updateTokenToIdMapping(tokenValue, NotLoginException.KICK_OUT);
            AuthEvent authEvent = new AuthEvent();
            authEvent.setEventType(AuthEvent.AuthEventType.KICKOUT);
            authEvent.setLoginId(loginId);
            authEvent.setTokenValue(tokenValue);
            getAuthEngine().trigger(authEvent);
        }, true);
    }

    /**
     * 踢人下线，根据指定 Token
     * <p> 当对方再次访问系统时，会抛出NotLoginException异常，场景值=-5 </p>
     *
     * @param tokenValue 指定token
     */
    default void kickoutByTokenValue(String tokenValue) {
        // 1. 清理 token-last-activity
        clearLastActivity(tokenValue);

        // 2. 不注销 Token-Session

        // if. 无效 loginId 立即返回
        String loginId = getLoginIdNotHandle(tokenValue);
        if (isValidLoginId(loginId) == false) {
            return;
        }

        // 3. 给token打上标记：被踢下线
        updateTokenToIdMapping(tokenValue, NotLoginException.KICK_OUT);

        // $$. 否则通知监听器，某某Token被踢下线了
        AuthEvent authEvent = new AuthEvent();
        authEvent.setEventType(AuthEvent.AuthEventType.KICKOUT);
        authEvent.setLoginId(loginId);
        authEvent.setTokenValue(tokenValue);
        getAuthEngine().trigger(authEvent);

        // 4. 清理User-Session上的token签名 & 尝试注销User-Session
        AuthSession session = getSessionByLoginId(loginId, false);
        if (session != null) {
            getAuthEngine().getAuthSessionTemplate().removeTokenSign(session, tokenValue);
            getAuthEngine().getAuthSessionTemplate().logoutByTokenSignCountToZero(session);
        }
    }

    /**
     * 顶人下线，根据账号id 和 设备标识
     * <p> 当对方再次访问系统时，会抛出NotLoginException异常，场景值=-4 </p>
     *
     * @param loginId 账号id
     * @param device  设备标识 (填null代表顶替所有设备)
     */
    default void replaced(Object loginId, String device) {
        clearTokenCommonMethod(loginId, device, tokenValue -> {
            // 将此 token 标记为已被顶替
            updateTokenToIdMapping(tokenValue, NotLoginException.BE_REPLACED);
            AuthEvent authEvent = new AuthEvent();
            authEvent.setEventType(AuthEvent.AuthEventType.RELACED);
            authEvent.setLoginId(loginId);
            authEvent.setTokenValue(tokenValue);
            getAuthEngine().trigger(authEvent);
        }, false);
    }

    /**
     * 封装 注销、踢人、顶人 三个动作的相同代码（无API含义方法）
     *
     * @param loginId         账号id
     * @param device          设备标识
     * @param appendFun       追加操作
     * @param isLogoutSession 是否注销 User-Session
     */
    default void clearTokenCommonMethod(Object loginId, String device, Consumer<String> appendFun, boolean isLogoutSession) {
        // 1. 如果此账号尚未登录，则不执行任何操作
        AuthSession session = getSessionByLoginId(loginId, false);
        if (session == null) {
            return;
        }
        // 2. 循环token签名列表，开始删除相关信息
        for (TokenSign tokenSign : session.getTokenSignList()) {
            if (device == null || tokenSign.getDevice().equals(device)) {
                // -------- 共有操作
                // s1. 获取token
                String tokenValue = tokenSign.getValue();
                // s2. 清理掉[token-last-activity]
                clearLastActivity(tokenValue);
                // s3. 从token签名列表移除
                getAuthEngine().getAuthSessionTemplate().removeTokenSign(session, tokenValue);
                // -------- 追加操作
                appendFun.accept(tokenValue);
            }
        }
        // 3. 尝试注销session
        if (isLogoutSession) {
            getAuthEngine().getAuthSessionTemplate().logoutByTokenSignCountToZero(session);
        }
    }

    // ---- 会话查询

    /**
     * 当前会话是否已经登录
     *
     * @return 是否已登录
     */
    default boolean isLogin() {
        // 判断条件：不为null，并且不在异常项集合里
        return getLoginIdDefaultNull() != null;
    }

    /**
     * 检验当前会话是否已经登录，如未登录，则抛出异常
     */
    default void checkLogin() {
        getLoginId();
    }

    /**
     * 获取当前会话账号id, 如果未登录，则抛出异常
     *
     * @return 账号id
     */
    default Object getLoginId() {
        // 如果正在[临时身份切换], 则返回临时身份
        if (isSwitch()) {
            return getSwitchLoginId();
        }
        // 如果获取不到token，则抛出: 无token
        String tokenValue = getTokenValue();
        if (tokenValue == null) {
            throw NotLoginException.newInstance(NotLoginException.NOT_TOKEN);
        }
        // 查找此token对应loginId, 如果找不到则抛出：无效token
        String loginId = getLoginIdNotHandle(tokenValue);
        if (loginId == null) {
            throw NotLoginException.newInstance(NotLoginException.INVALID_TOKEN, tokenValue);
        }
        // 如果是已经过期，则抛出：已经过期
        if (loginId.equals(NotLoginException.TOKEN_TIMEOUT)) {
            throw NotLoginException.newInstance(NotLoginException.TOKEN_TIMEOUT, tokenValue);
        }
        // 如果是已经被顶替下去了, 则抛出：已被顶下线
        if (loginId.equals(NotLoginException.BE_REPLACED)) {
            throw NotLoginException.newInstance(NotLoginException.BE_REPLACED, tokenValue);
        }
        // 如果是已经被踢下线了, 则抛出：已被踢下线
        if (loginId.equals(NotLoginException.KICK_OUT)) {
            throw NotLoginException.newInstance(NotLoginException.KICK_OUT, tokenValue);
        }
        // 检查是否已经 [临时过期]
        checkActivityTimeout(tokenValue);
        // 如果配置了自动续签, 则: 更新[最后操作时间]
        if (getConfig().getAutoRenew()) {
            updateLastActivityToNow(tokenValue);
        }
        // 至此，返回loginId
        return loginId;
    }

    /**
     * 获取当前会话账号id, 如果未登录，则返回默认值
     *
     * @param <T>          返回类型
     * @param defaultValue 默认值
     * @return 登录id
     */
    @SuppressWarnings("unchecked")
    default <T> T getLoginId(T defaultValue) {
        Object loginId = getLoginIdDefaultNull();
        // 如果loginId为null，则返回默认值
        if (loginId == null) {
            return defaultValue;
        }
        // 开始尝试类型转换，只尝试三种类型：int、long、String
        if (defaultValue instanceof Integer) {
            return (T) Integer.valueOf(loginId.toString());
        }
        if (defaultValue instanceof Long) {
            return (T) Long.valueOf(loginId.toString());
        }
        if (defaultValue instanceof String) {
            return (T) loginId.toString();
        }
        return (T) loginId;
    }

    /**
     * 获取当前会话账号id, 如果未登录，则返回null
     *
     * @return 账号id
     */
    default Object getLoginIdDefaultNull() {
        // 如果正在[临时身份切换]
        if (isSwitch()) {
            return getSwitchLoginId();
        }
        // 如果连token都是空的，则直接返回
        String tokenValue = getTokenValue();
        if (tokenValue == null) {
            return null;
        }
        // loginId为null或者在异常项里面，均视为未登录, 返回null
        Object loginId = getLoginIdNotHandle(tokenValue);
        if (isValidLoginId(loginId) == false) {
            return null;
        }
        // 如果已经[临时过期]
        if (getTokenActivityTimeoutByToken(tokenValue) == AuthTokenDao.NOT_VALUE_EXPIRE) {
            return null;
        }
        // 执行到此，证明loginId已经是个正常的账号id了
        return loginId;
    }

    /**
     * 获取当前会话账号id, 并转换为String类型
     *
     * @return 账号id
     */
    default String getLoginIdAsString() {
        return String.valueOf(getLoginId());
    }

    /**
     * 获取当前会话账号id, 并转换为int类型
     *
     * @return 账号id
     */
    default int getLoginIdAsInt() {
        return Integer.parseInt(String.valueOf(getLoginId()));
    }

    /**
     * 获取当前会话账号id, 并转换为long类型
     *
     * @return 账号id
     */
    default long getLoginIdAsLong() {
        return Long.parseLong(String.valueOf(getLoginId()));
    }

    /**
     * 获取指定Token对应的账号id，如果未登录，则返回 null
     *
     * @param tokenValue token
     * @return 账号id
     */
    default Object getLoginIdByToken(String tokenValue) {
        // token为空时，直接返回null
        if (FoxUtil.isEmpty(tokenValue)) {
            return null;
        }
        // loginId为无效值时，直接返回null
        String loginId = getLoginIdNotHandle(tokenValue);
        if (isValidLoginId(loginId) == false) {
            return null;
        }
        //
        return loginId;
    }

    /**
     * 获取指定Token对应的账号id (不做任何特殊处理)
     *
     * @param tokenValue token值
     * @return 账号id
     */
    default String getLoginIdNotHandle(String tokenValue) {
        return getAuthTokenDao().get(splicingKeyTokenValue(tokenValue));
    }

    /**
     * 获取Token扩展信息（只在jwt模式下有效）
     *
     * @param key 键值
     * @return 对应的扩展数据
     */
    default Object getExtra(String key) {
        throw new ApiDisabledException();
    }


    // ---- 其它操作

    /**
     * 判断一个 loginId 是否是有效的
     *
     * @param loginId 账号id
     * @return /
     */
    default boolean isValidLoginId(Object loginId) {
        return loginId != null && !NotLoginException.ABNORMAL_LIST.contains(loginId.toString());
    }

    /**
     * 删除 Token-Id 映射
     *
     * @param tokenValue token值
     */
    default void deleteTokenToIdMapping(String tokenValue) {
        getAuthTokenDao().delete(splicingKeyTokenValue(tokenValue));
    }

    /**
     * 更改 Token 指向的 账号Id 值
     *
     * @param tokenValue token值
     * @param loginId    新的账号Id值
     */
    default void updateTokenToIdMapping(String tokenValue, Object loginId) {
        TokenException.throwBy(FoxUtil.isEmpty(loginId), "LoginId 不能为空");
        getAuthTokenDao().update(splicingKeyTokenValue(tokenValue), loginId.toString());
    }

    /**
     * 存储 Token-Id 映射
     *
     * @param tokenValue token值
     * @param loginId    账号id
     * @param timeout    会话有效期 (单位: 秒)
     */
    default void saveTokenToIdMapping(String tokenValue, Object loginId, long timeout) {
        getAuthTokenDao().set(splicingKeyTokenValue(tokenValue), String.valueOf(loginId), timeout);
    }


    // ------------------- User-Session 相关 -------------------

    /**
     * 获取指定key的Session, 如果Session尚未创建，isCreate=是否新建并返回
     *
     * @param sessionId SessionId
     * @param isCreate  是否新建
     * @return Session对象
     */
    default AuthSession getSessionBySessionId(String sessionId, boolean isCreate) {
        AuthSession session = getAuthTokenDao().getSession(sessionId);
        if (session == null && isCreate) {
            session = getAuthEngine().getAuthStrategy().createSession.apply(sessionId);
            getAuthTokenDao().setSession(session, getConfig().getTimeout());
        }
        return session;
    }

    /**
     * 获取指定key的Session, 如果Session尚未创建，则返回null
     *
     * @param sessionId SessionId
     * @return Session对象
     */
    default AuthSession getSessionBySessionId(String sessionId) {
        return getSessionBySessionId(sessionId, false);
    }

    /**
     * 获取指定账号id的User-Session, 如果Session尚未创建，isCreate=是否新建并返回
     *
     * @param loginId  账号id
     * @param isCreate 是否新建
     * @return Session对象
     */
    default AuthSession getSessionByLoginId(Object loginId, boolean isCreate) {
        return getSessionBySessionId(splicingKeySession(loginId), isCreate);
    }

    /**
     * 获取指定账号id的User-Session，如果Session尚未创建，则新建并返回
     *
     * @param loginId 账号id
     * @return Session对象
     */
    default AuthSession getSessionByLoginId(Object loginId) {
        return getSessionBySessionId(splicingKeySession(loginId), true);
    }

    /**
     * 获取当前User-Session, 如果Session尚未创建，isCreate=是否新建并返回
     *
     * @param isCreate 是否新建
     * @return Session对象
     */
    default AuthSession getSession(boolean isCreate) {
        return getSessionByLoginId(getLoginId(), isCreate);
    }

    /**
     * 获取当前User-Session，如果Session尚未创建，则新建并返回
     *
     * @return Session对象
     */
    default AuthSession getSession() {
        return getSession(true);
    }


    // ------------------- Token-Session 相关 -------------------

    /**
     * 获取指定Token-Session，如果Session尚未创建，isCreate代表是否新建并返回
     *
     * @param tokenValue token值
     * @param isCreate   是否新建
     * @return session对象
     */
    default AuthSession getTokenSessionByToken(String tokenValue, boolean isCreate) {
        return getSessionBySessionId(splicingKeyTokenSession(tokenValue), isCreate);
    }

    /**
     * 获取指定Token-Session，如果Session尚未创建，则新建并返回
     *
     * @param tokenValue Token值
     * @return Session对象
     */
    default AuthSession getTokenSessionByToken(String tokenValue) {
        return getSessionBySessionId(splicingKeyTokenSession(tokenValue), true);
    }

    /**
     * 获取当前Token-Session，如果Session尚未创建，isCreate代表是否新建并返回
     *
     * @param isCreate 是否新建
     * @return Session对象
     */
    default AuthSession getTokenSession(boolean isCreate) {
        // 如果配置了需要校验登录状态，则验证一下
        if (getConfig().getTokenSessionCheckLogin()) {
            checkLogin();
        } else {
            // 如果配置忽略token登录校验，则必须保证token不为null (token为null的时候随机创建一个)
            String tokenValue = getTokenValue();
            if (tokenValue == null || Objects.equals(tokenValue, "")) {
                // 随机一个token送给Ta
                tokenValue = createTokenValue(null, null, getConfig().getTimeout(), null);
                // 写入 [最后操作时间]
                setLastActivityToNow(tokenValue);
                // 在当前会话写入这个tokenValue
                int cookieTimeout = (int) (getConfig().getTimeout() == AuthTokenDao.NEVER_EXPIRE ? Integer.MAX_VALUE : getConfig().getTimeout());
                setTokenValue(tokenValue, cookieTimeout);
            }
        }
        // 返回这个token对应的Token-Session
        return getSessionBySessionId(splicingKeyTokenSession(getTokenValue()), isCreate);
    }

    /**
     * 获取当前Token-Session，如果Session尚未创建，则新建并返回
     *
     * @return Session对象
     */
    default AuthSession getTokenSession() {
        return getTokenSession(true);
    }

    /**
     * 删除Token-Session
     *
     * @param tokenValue token值
     */
    default void deleteTokenSession(String tokenValue) {
        getAuthTokenDao().delete(splicingKeyTokenSession(tokenValue));
    }

    // ------------------- [临时有效期] 验证相关 -------------------

    /**
     * 写入指定token的 [最后操作时间] 为当前时间戳
     *
     * @param tokenValue 指定token
     */
    default void setLastActivityToNow(String tokenValue) {
        // 如果token == null 或者 设置了[永不过期], 则立即返回
        if (tokenValue == null || isOpenActivityCheck() == false) {
            return;
        }
        // 将[最后操作时间]标记为当前时间戳
        getAuthTokenDao().set(splicingKeyLastActivityTime(tokenValue), String.valueOf(System.currentTimeMillis()), getConfig().getTimeout());
    }

    /**
     * 清除指定token的 [最后操作时间]
     *
     * @param tokenValue 指定token
     */
    default void clearLastActivity(String tokenValue) {
        // 如果token == null 或者 设置了[永不过期], 则立即返回
        if (tokenValue == null || isOpenActivityCheck() == false) {
            return;
        }
        // 删除[最后操作时间]
        getAuthTokenDao().delete(splicingKeyLastActivityTime(tokenValue));
        // 清除标记
        getAuthEngine().getAuthCheckEngine().getAuthTokenContext().getStorage().delete(TokenConsts.TOKEN_ACTIVITY_TIMEOUT_CHECKED_KEY);
    }

    /**
     * 检查指定token 是否已经[临时过期]，如果已经过期则抛出异常
     *
     * @param tokenValue 指定token
     */
    default void checkActivityTimeout(String tokenValue) {
        // 如果token == null 或者 设置了[永不过期], 则立即返回
        if (tokenValue == null || isOpenActivityCheck() == false) {
            return;
        }
        // 如果本次请求已经有了[检查标记], 则立即返回
        AuthStorage storage = getAuthEngine().getAuthCheckEngine().getAuthTokenContext().getStorage();
        if (storage.get(TokenConsts.TOKEN_ACTIVITY_TIMEOUT_CHECKED_KEY) != null) {
            return;
        }
        // ------------ 验证是否已经 [临时过期]
        // 获取 [临时剩余时间]
        long timeout = getTokenActivityTimeoutByToken(tokenValue);
        // -1 代表此token已经被设置永不过期，无须继续验证
        if (timeout == AuthTokenDao.NEVER_EXPIRE) {
            return;
        }
        // -2 代表已过期，抛出异常
        if (timeout == AuthTokenDao.NOT_VALUE_EXPIRE) {
            throw NotLoginException.newInstance(NotLoginException.TOKEN_TIMEOUT, tokenValue);
        }
        // --- 至此，验证已通过

        // 打上[检查标记]，标记一下当前请求已经通过验证，避免一次请求多次验证，造成不必要的性能消耗
        storage.set(TokenConsts.TOKEN_ACTIVITY_TIMEOUT_CHECKED_KEY, true);
    }

    /**
     * 检查当前token 是否已经[临时过期]，如果已经过期则抛出异常
     */
    default void checkActivityTimeout() {
        checkActivityTimeout(getTokenValue());
    }

    /**
     * 续签指定token：(将 [最后操作时间] 更新为当前时间戳)
     *
     * @param tokenValue 指定token
     */
    default void updateLastActivityToNow(String tokenValue) {
        // 如果token == null 或者 设置了[永不过期], 则立即返回
        if (tokenValue == null || isOpenActivityCheck() == false) {
            return;
        }
        getAuthTokenDao().update(splicingKeyLastActivityTime(tokenValue), String.valueOf(System.currentTimeMillis()));
    }

    /**
     * 续签当前token：(将 [最后操作时间] 更新为当前时间戳)
     * <h1>请注意: 即时token已经 [临时过期] 也可续签成功，
     * 如果此场景下需要提示续签失败，可在此之前调用 checkActivityTimeout() 强制检查是否过期即可 </h1>
     */
    default void updateLastActivityToNow() {
        updateLastActivityToNow(getTokenValue());
    }

    // ------------------- 过期时间相关 -------------------

    /**
     * 获取当前登录者的 token 剩余有效时间 (单位: 秒)
     *
     * @return token剩余有效时间
     */
    default long getTokenTimeout() {
        return getAuthTokenDao().getTimeout(splicingKeyTokenValue(getTokenValue()));
    }

    /**
     * 获取指定 loginId 的 token 剩余有效时间 (单位: 秒)
     *
     * @param loginId 指定loginId
     * @return token剩余有效时间
     */
    default long getTokenTimeoutByLoginId(Object loginId) {
        return getAuthTokenDao().getTimeout(splicingKeyTokenValue(getTokenValueByLoginId(loginId)));
    }

    /**
     * 获取当前登录者的 User-Session 剩余有效时间 (单位: 秒)
     *
     * @return token剩余有效时间
     */
    default long getSessionTimeout() {
        return getSessionTimeoutByLoginId(getLoginIdDefaultNull());
    }

    /**
     * 获取指定 loginId 的 User-Session 剩余有效时间 (单位: 秒)
     *
     * @param loginId 指定loginId
     * @return token剩余有效时间
     */
    default long getSessionTimeoutByLoginId(Object loginId) {
        return getAuthTokenDao().getSessionTimeout(splicingKeySession(loginId));
    }

    /**
     * 获取当前 Token-Session 剩余有效时间 (单位: 秒)
     *
     * @return token剩余有效时间
     */
    default long getTokenSessionTimeout() {
        return getTokenSessionTimeoutByTokenValue(getTokenValue());
    }

    /**
     * 获取指定 Token-Session 剩余有效时间 (单位: 秒)
     *
     * @param tokenValue 指定token
     * @return token剩余有效时间
     */
    default long getTokenSessionTimeoutByTokenValue(String tokenValue) {
        return getAuthTokenDao().getSessionTimeout(splicingKeyTokenSession(tokenValue));
    }

    /**
     * 获取当前 token [临时过期] 剩余有效时间 (单位: 秒)
     *
     * @return token [临时过期] 剩余有效时间
     */
    default long getTokenActivityTimeout() {
        return getTokenActivityTimeoutByToken(getTokenValue());
    }

    /**
     * 获取指定 token [临时过期] 剩余有效时间 (单位: 秒)
     *
     * @param tokenValue 指定token
     * @return token[临时过期]剩余有效时间
     */
    default long getTokenActivityTimeoutByToken(String tokenValue) {
        // 如果token为null , 则返回 -2
        if (tokenValue == null) {
            return AuthTokenDao.NOT_VALUE_EXPIRE;
        }
        // 如果设置了永不过期, 则返回 -1
        if (isOpenActivityCheck() == false) {
            return AuthTokenDao.NEVER_EXPIRE;
        }
        // ------ 开始查询
        // 获取相关数据
        String keyLastActivityTime = splicingKeyLastActivityTime(tokenValue);
        String lastActivityTimeString = getAuthTokenDao().get(keyLastActivityTime);
        // 查不到，返回-2
        if (lastActivityTimeString == null) {
            return AuthTokenDao.NOT_VALUE_EXPIRE;
        }
        // 计算相差时间
        long lastActivityTime = Long.parseLong(lastActivityTimeString);
        long apartSecond = (System.currentTimeMillis() - lastActivityTime) / 1000;
        long timeout = getConfig().getActivityTimeout() - apartSecond;
        // 如果 < 0， 代表已经过期 ，返回-2
        if (timeout < 0) {
            return AuthTokenDao.NOT_VALUE_EXPIRE;
        }
        return timeout;
    }

    /**
     * 对当前 Token 的 timeout 值进行续期
     *
     * @param timeout 要修改成为的有效时间 (单位: 秒)
     */
    default void renewTimeout(long timeout) {
        // 续期 db 数据
        String tokenValue = getTokenValue();
        renewTimeout(tokenValue, timeout);
        // 续期客户端Cookie有效期
        if (getAuthEngine().getAuthCheckEngine().getConfig().getIsReadCookie()) {
            setTokenValueToCookie(tokenValue, (int) timeout);
        }
    }

    /**
     * 对指定 Token 的 timeout 值进行续期
     *
     * @param tokenValue 指定token
     * @param timeout    要修改成为的有效时间 (单位: 秒)
     */
    default void renewTimeout(String tokenValue, long timeout) {

        // Token 指向的 LoginId 异常时，不进行任何操作
        Object loginId = getLoginIdByToken(tokenValue);
        if (loginId == null) {
            return;
        }

        AuthTokenDao dao = getAuthTokenDao();

        // 续期 Token 有效期
        dao.updateTimeout(splicingKeyTokenValue(tokenValue), timeout);

        // 续期 Token-Session 有效期
        AuthSession tokenSession = getTokenSessionByToken(tokenValue, false);
        if (tokenSession != null) {
            getAuthEngine().getAuthSessionTemplate().updateTimeout(tokenSession.getId(), timeout);
        }

        // 续期指向的 User-Session 有效期
        getAuthEngine().getAuthSessionTemplate().updateMinTimeout(getSessionByLoginId(loginId).getId(), timeout);

        // Token-Activity 活跃检查相关
        if (isOpenActivityCheck()) {
            dao.updateTimeout(splicingKeyLastActivityTime(tokenValue), timeout);
        }
    }

    // ------------------- id 反查 token 相关操作 -------------------

    /**
     * 获取指定账号id的tokenValue
     * <p> 在配置为允许并发登录时，此方法只会返回队列的最后一个token，
     * 如果你需要返回此账号id的所有token，请调用 getTokenValueListByLoginId
     *
     * @param loginId 账号id
     * @return token值
     */
    default String getTokenValueByLoginId(Object loginId) {
        return getTokenValueByLoginId(loginId, null);
    }

    /**
     * 获取指定账号id指定设备端的tokenValue
     * <p> 在配置为允许并发登录时，此方法只会返回队列的最后一个token，
     * 如果你需要返回此账号id的所有token，请调用 getTokenValueListByLoginId
     *
     * @param loginId 账号id
     * @param device  设备标识，填null代表不限设备
     * @return token值
     */
    default String getTokenValueByLoginId(Object loginId, String device) {
        List<String> tokenValueList = getTokenValueListByLoginId(loginId, device);
        return tokenValueList.size() == 0 ? null : tokenValueList.get(tokenValueList.size() - 1);
    }

    /**
     * 获取指定账号id的tokenValue集合
     *
     * @param loginId 账号id
     * @return 此loginId的所有相关token
     */
    default List<String> getTokenValueListByLoginId(Object loginId) {
        return getTokenValueListByLoginId(loginId, null);
    }

    /**
     * 获取指定账号id指定设备端的tokenValue 集合
     *
     * @param loginId 账号id
     * @param device  设备标识，填null代表不限设备
     * @return 此loginId的所有相关token
     */
    default List<String> getTokenValueListByLoginId(Object loginId, String device) {
        // 如果session为null的话直接返回空集合
        AuthSession session = getSessionByLoginId(loginId, false);
        if (session == null) {
            return Collections.emptyList();
        }
        // 遍历解析
        List<TokenSign> tokenSignList = session.getTokenSignList();
        List<String> tokenValueList = new ArrayList<>();
        for (TokenSign tokenSign : tokenSignList) {
            if (device == null || tokenSign.getDevice().equals(device)) {
                tokenValueList.add(tokenSign.getValue());
            }
        }
        return tokenValueList;
    }

    /**
     * 返回当前会话的登录设备
     *
     * @return 当前令牌的登录设备
     */
    default String getLoginDevice() {
        // 如果没有token，直接返回 null
        String tokenValue = getTokenValue();
        if (tokenValue == null) {
            return null;
        }
        // 如果还未登录，直接返回 null
        if (!isLogin()) {
            return null;
        }
        // 如果session为null的话直接返回 null
        AuthSession session = getSessionByLoginId(getLoginIdDefaultNull(), false);
        if (session == null) {
            return null;
        }
        // 遍历解析
        List<TokenSign> tokenSignList = session.getTokenSignList();
        for (TokenSign tokenSign : tokenSignList) {
            if (tokenSign.getValue().equals(tokenValue)) {
                return tokenSign.getDevice();
            }
        }
        return null;
    }


    // ------------------- 会话管理 -------------------

    /**
     * 根据条件查询Token
     *
     * @param keyword 关键字
     * @param start   开始处索引 (-1代表查询所有)
     * @param size    获取数量
     * @return token集合
     */
    default List<String> searchTokenValue(String keyword, int start, int size) {
        return getAuthTokenDao().searchData(splicingKeyTokenValue(""), keyword, start, size);
    }

    /**
     * 根据条件查询SessionId
     *
     * @param keyword 关键字
     * @param start   开始处索引 (-1代表查询所有)
     * @param size    获取数量
     * @return sessionId集合
     */
    default List<String> searchSessionId(String keyword, int start, int size) {
        return getAuthTokenDao().searchData(splicingKeySession(""), keyword, start, size);
    }

    /**
     * 根据条件查询Token专属Session的Id
     *
     * @param keyword 关键字
     * @param start   开始处索引 (-1代表查询所有)
     * @param size    获取数量
     * @return sessionId集合
     */
    default List<String> searchTokenSessionId(String keyword, int start, int size) {
        return getAuthTokenDao().searchData(splicingKeyTokenSession(""), keyword, start, size);
    }

    // ------------------- 账号封禁 -------------------

    /**
     * 封禁指定账号
     * <p> 此方法不会直接将此账号id踢下线，而是在对方再次登录时抛出`DisableLoginException`异常
     *
     * @param loginId     指定账号id
     * @param disableTime 封禁时间, 单位: 秒 （-1=永久封禁）
     */
    default void disable(Object loginId, long disableTime) {
        // 标注为已被封禁
        getAuthTokenDao().set(splicingKeyDisable(loginId), DisableLoginException.BE_VALUE, disableTime);

        // $$ 通知监听器
        AuthEvent authEvent = new AuthEvent();
        authEvent.setEventType(AuthEvent.AuthEventType.DISABLE);
        authEvent.setLoginId(loginId);
        authEvent.setDisableTime(disableTime);
        getAuthEngine().trigger(authEvent);
    }

    /**
     * 指定账号是否已被封禁 (true=已被封禁, false=未被封禁)
     *
     * @param loginId 账号id
     * @return see note
     */
    default boolean isDisable(Object loginId) {
        return getAuthTokenDao().get(splicingKeyDisable(loginId)) != null;
    }

    /**
     * 获取指定账号剩余封禁时间，单位：秒（-1=永久封禁，-2=未被封禁）
     *
     * @param loginId 账号id
     * @return see note
     */
    default long getDisableTime(Object loginId) {
        return getAuthTokenDao().getTimeout(splicingKeyDisable(loginId));
    }

    /**
     * 解封指定账号
     *
     * @param loginId 账号id
     */
    default void untieDisable(Object loginId) {
        getAuthTokenDao().delete(splicingKeyDisable(loginId));
        // $$ 通知监听器
        AuthEvent authEvent = new AuthEvent();
        authEvent.setLoginId(loginId);
        authEvent.setEventType(AuthEvent.AuthEventType.UNTIE_DISABLE);
        getAuthEngine().trigger(authEvent);
    }

    // ------------------- 身份切换 -------------------

    /**
     * 临时切换身份为指定账号id
     *
     * @param loginId 指定loginId
     */
    default void switchTo(Object loginId) {
        getAuthEngine().getAuthCheckEngine().getAuthTokenContext().getStorage().set(splicingKeySwitch(), loginId);
    }

    /**
     * 结束临时切换身份
     */
    default void endSwitch() {
        getAuthEngine().getAuthCheckEngine().getAuthTokenContext().getStorage().delete(splicingKeySwitch());
    }

    /**
     * 当前是否正处于[身份临时切换]中
     *
     * @return 是否正处于[身份临时切换]中
     */
    default boolean isSwitch() {
        return getAuthEngine().getAuthCheckEngine().getAuthTokenContext().getStorage().get(splicingKeySwitch()) != null;
    }

    /**
     * 返回[身份临时切换]的loginId
     *
     * @return 返回[身份临时切换]的loginId
     */
    default Object getSwitchLoginId() {
        return getAuthEngine().getAuthCheckEngine().getAuthTokenContext().getStorage().get(splicingKeySwitch());
    }

    /**
     * 在一个代码段里方法内，临时切换身份为指定账号id
     *
     * @param loginId  指定账号id
     * @param function 要执行的方法
     */
    default void switchTo(Object loginId, AuthFunction function) {
        try {
            switchTo(loginId);
            function.run();
        } catch (Exception e) {
            throw e;
        } finally {
            endSwitch();
        }
    }

    // ------------------- 返回相应key -------------------

    /**
     * 拼接key：客户端 tokenName
     *
     * @return key
     */
    default String splicingKeyTokenName() {
        return getConfig().getTokenName();
    }

    /**
     * 拼接key： tokenValue 持久化 token-id
     *
     * @param tokenValue token值
     * @return key
     */
    default String splicingKeyTokenValue(String tokenValue) {
        return getConfig().getTokenName() + ":token:" + tokenValue;
    }

    /**
     * 拼接key： Session 持久化
     *
     * @param loginId 账号id
     * @return key
     */
    default String splicingKeySession(Object loginId) {
        return getConfig().getTokenName() + ":session:" + loginId;
    }

    /**
     * 拼接key： tokenValue的Token-Session
     *
     * @param tokenValue token值
     * @return key
     */
    default String splicingKeyTokenSession(String tokenValue) {
        return getConfig().getTokenName() + ":token-session:" + tokenValue;
    }

    /**
     * 拼接key： 指定token的最后操作时间 持久化
     *
     * @param tokenValue token值
     * @return key
     */
    default String splicingKeyLastActivityTime(String tokenValue) {
        return getConfig().getTokenName() + ":last-activity:" + tokenValue;
    }

    /**
     * 在进行身份切换时，使用的存储key
     *
     * @return key
     */
    default String splicingKeySwitch() {
        return TokenConsts.SWITCH_TO_SAVE_KEY;
    }

    /**
     * 如果token为本次请求新创建的，则以此字符串为key存储在当前request中
     *
     * @return key
     */
    default String splicingKeyJustCreatedSave() {
//		return SaTokenConsts.JUST_CREATED_SAVE_KEY + loginType;
        return TokenConsts.JUST_CREATED;
    }

    /**
     * 拼接key： 账号封禁
     *
     * @param loginId 账号id
     * @return key
     */
    default String splicingKeyDisable(Object loginId) {
        return getConfig().getTokenName() + ":disable:" + loginId;
    }


    // ------------------- Bean对象代理 -------------------

    /**
     * 返回全局配置对象
     *
     * @return /
     */
    default TokenConfig getConfig() {
        // 为什么再次代理一层? 为某些极端业务场景下[需要不同StpLogic不同配置]提供便利
        return getAuthEngine().getConfig();
    }

    /**
     * 返回全局配置对象的isShare属性
     *
     * @return /
     */
    default boolean getConfigOfIsShare() {
        return getConfig().getIsShare();
    }

    /**
     * 返回全局配置是否开启了Token 活跃校验
     *
     * @return /
     */
    default boolean isOpenActivityCheck() {
        return getConfig().getActivityTimeout() != AuthTokenDao.NEVER_EXPIRE;
    }

    /**
     * 返回持久化对象
     *
     * @return /
     */
    default AuthTokenDao getAuthTokenDao() {
        return getAuthEngine().getAuthTokenDao();
    }

}
